package org.rcgwt.client.ui.calendar.renderer.impl;

import org.rcgwt.client.ui.calendar.controller.CalendarAction;
import org.rcgwt.client.ui.calendar.controller.CalendarChangesMask;
import org.rcgwt.client.ui.calendar.controller.CalendarController;
import org.rcgwt.client.ui.calendar.event.ModelChangedEvent;
import org.rcgwt.client.ui.calendar.event.ModelChangedHandler;
import org.rcgwt.client.ui.calendar.i18n.CalendarUtils;
import org.rcgwt.client.ui.calendar.model.impl.CalendarModel;
import org.rcgwt.client.ui.calendar.model.impl.SelectionType;
import org.rcgwt.client.ui.calendar.mvc.ICalendarContext;
import org.rcgwt.client.ui.calendar.renderer.IHeaderRenderer;
import org.rcgwt.client.ui.themes.ITheme;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.MouseWheelEvent;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HTMLTable.Cell;

/**
 * A popup year and month cursor (one per selection type = month & year)
 * 
 * @author Elian ORIOU
 * 
 */

public class PopupCalendarHeaderRenderer implements IHeaderRenderer {

	/* Constants */
	private static final String[] months = CalendarUtils.getMonths();
	private static final int LAST_DISPLAYED_YEAR_ROW = 5;

	private static final int PREVIOUS_YEAR_RANGE = 5;
	private static final int NEXT_YEAR_RANGE = 6;

	/* Controls */
	private CalendarModel model;
	private CalendarController controller;
	private ITheme theme;

	private Label l_year;
	private Label l_month;

	private Label b_prev;
	private Label b_next;

	private Grid yearGrid;
	private Grid monthGrid;

	private FlexTable table;
	private PopupPanel yearPopup;
	private PopupPanel monthPopup;

	private int prevYearRow;
	private int prevYearCell;

	private int prevMonthRow;
	private int prevMonthCell;

	@Override
	public Widget render(ICalendarContext context) {
		model = context.getModel();
		controller = context.getController();
		theme = context.getTheme();

		prevYearRow = -1;
		prevYearCell = -1;
		prevMonthRow = -1;
		prevMonthCell = -1;

		table = new FlexTable();
		table.addStyleName(theme.getYearAndMonthPanelStyle().getClassName());

		yearPopup = new PopupPanel();
		yearPopup.addStyleName(theme.getPopupPanelStyle().getClassName());
		yearPopup.setVisible(false);
		yearPopup.setAnimationEnabled(true);
		yearPopup.setAutoHideEnabled(true);

		monthPopup = new PopupPanel();
		monthPopup.addStyleName(theme.getPopupPanelStyle().getClassName());
		monthPopup.setVisible(false);
		monthPopup.setAnimationEnabled(true);
		monthPopup.setAutoHideEnabled(true);

		l_year = new Label();
		l_year.addStyleName(theme.getYearLabelStyle().getClassName());

		l_month = new Label();
		l_month.addStyleName(theme.getMonthLabelStyle().getClassName());

		table.setWidget(0, 0, l_year);
		table.setWidget(1, 0, l_month);

		createPopupContent();
		createHandlers();

		return table;
	}

	private void createPopupContent() {
		yearGrid = new Grid(7, 2);
		yearGrid.addStyleName(theme.getPopupYearGridStyle().getClassName());

		monthGrid = new Grid(6, 2);
		monthGrid.addStyleName(theme.getPopupYearGridStyle().getClassName());

		int start = getDisplayedYear() - PREVIOUS_YEAR_RANGE;
		int end = getDisplayedYear() + NEXT_YEAR_RANGE;

		fillYearGrid(start, end);

		b_prev = new Label();
		b_prev.addStyleName(theme.getPreviousButtonStyle().getClassName());
		b_next = new Label();
		b_next.addStyleName(theme.getNextButtonStyle().getClassName());

		yearGrid.setWidget(6, 0, b_prev);
		yearGrid.setWidget(6, 1, b_next);

		yearPopup.setWidget(yearGrid);

		fillMonthGrid();
	}

	private void fillYearGrid(int start, int end) {
		int row = 0;
		int col = 0;
		int displayableMonths = 0;

		for (int i = start; i <= end; i++) {
			Label l = (Label) yearGrid.getWidget(row, col);
			if (l == null) {
				l = new Label();
			}
			l.setText("" + i);
			displayableMonths = 0;
			for (int j = 0; j < 12; j++) {
				if (controller.isDisplayableMin(j, i, 0) == true
						&& controller.isDisplayableMax(j, i, 0) == true) {
					displayableMonths++;
				}
			}
			if (displayableMonths == 0) {
				l.setStyleName(theme.getForbiddenStyle().getClassName());
			} else {
				l.setStyleName(theme.getYearLabelStyle().getClassName());
			}
			yearGrid.setWidget(row, col, l);
			if (row == yearGrid.getRowCount() - 2) {
				col = 1;
				row = 0;
			} else {
				row++;
			}
		}
	}

	private void fillMonthGrid() {
		int col = 0;
		int row = 0;

		for (int i = 0; i < months.length; i++) {
			String m = months[i];
			Label l = new Label(m);
			l.addStyleName(theme.getMonthLabelStyle().getClassName());
			monthGrid.setWidget(row, col, l);
			if (row == monthGrid.getRowCount() - 1) {
				col = 1;
				row = 0;
			} else {
				row++;
			}
		}
		monthPopup.setWidget(monthGrid);
	}

	private void createHandlers() {

		l_year.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				closePopups();
				deselectWidget(l_month);
				selectWidget(l_year);
				int start = getDisplayedYear() - PREVIOUS_YEAR_RANGE;
				int end = getDisplayedYear() + NEXT_YEAR_RANGE;
				fillYearGrid(start, end);
				selectWidget(getYearLabel(Integer.parseInt(l_year.getText())));
				yearPopup.showRelativeTo(l_year);
			}
		});
		l_month.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				closePopups();
				int year = model.getDisplayedYear();
				for (int i = 0; i < 12; i++) {
					int row = getRowForMonth(i);
					int col = getColForMonth(i);
					Widget w = monthGrid.getWidget(row, col);
					if (controller.isDisplayableMin(i, year, 0) == false
							|| controller.isDisplayableMax(i, year, 0) == false) {
						w
								.setStyleName(theme.getForbiddenStyle()
										.getClassName());
					} else {
						w.setStyleName(theme.getMonthLabelStyle()
								.getClassName());
					}
				}
				deselectWidget(l_year);
				selectWidget(l_month);
				selectWidget(getMonthLabel(l_month.getText()));
				monthPopup.showRelativeTo(l_month);
			}
		});
		controller.addModelChangedHandler(new ModelChangedHandler() {
			@Override
			public void onModelChangedEvent(ModelChangedEvent event) {
				int type = event.getElementType();
				int month = getDisplayedMonth();
				int year = getDisplayedYear();

				if ((type & CalendarChangesMask.SPECIFIED_MONTH) > 0) {
					l_month.setText(months[month]);
				}
				if ((type & CalendarChangesMask.SPECIFIED_YEAR) > 0) {
					l_year.setText(String.valueOf(year));
				}
				if ((type & (CalendarChangesMask.SPECIFIED_YEAR | CalendarChangesMask.SPECIFIED_MONTH)) > 0) {
					l_year.setText(String.valueOf(year));
					l_month.setText(months[month]);
				}
				if ((type & CalendarChangesMask.CURRENT_DAY) > 0) {
					l_year.setText(String.valueOf(year));
					l_month.setText(months[month]);
				}
				if ((event.getElementType() & CalendarChangesMask.SELECT_MONTH) > 0) {
					if (isSelected(l_year) == true) {
						deselectWidget(l_year);
					}
					selectWidget(l_month);
				}
				if ((event.getElementType() & CalendarChangesMask.UNSELECT_MONTH) > 0) {
					deselectWidget(l_month);
				}
			}
		});

		yearGrid.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				Cell c = yearGrid.getCellForEvent(event);
				if (c == null) {
					return;
				}
				int row = c.getRowIndex();
				int cell = c.getCellIndex();
				if (row > LAST_DISPLAYED_YEAR_ROW) {
					return;
				}
				if (prevYearRow != -1 && prevYearCell != -1) {
					deselectWidget(yearGrid
							.getWidget(prevYearRow, prevYearCell));
				} else {
					deselectWidget(getYearLabel(model.getDisplayedYear()));
				}
				prevYearRow = row;
				prevYearCell = cell;

				int year = getSelectedYear(row, cell);
				if (controller.setDisplayedYear(year) == true) {
					controller.setSelectionType(SelectionType.YEAR);
					controller.handle(CalendarAction.SELECTION_TYPE);
					controller.handle(CalendarAction.REFRESH_CALENDAR);
					closePopups();
				}
			}
		});

		monthGrid.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				Cell c = monthGrid.getCellForEvent(event);
				if (c == null) {
					return;
				}
				if (prevMonthRow != -1 && prevMonthCell != -1) {
					deselectWidget(monthGrid.getWidget(prevMonthRow,
							prevMonthCell));
				}
				deselectWidget(getMonthLabel(months[model.getDisplayedMonth()]));
				int row = c.getRowIndex();
				int cell = c.getCellIndex();
				prevMonthRow = row;
				prevMonthCell = cell;

				int month = getSelectedMonth(row, cell);
				if (controller.setDisplayedMonth(month) == true) {
					controller.setSelectionType(SelectionType.MONTH);
					controller.handle(CalendarAction.SELECTION_TYPE);
					controller.handle(CalendarAction.REFRESH_CALENDAR);
					closePopups();
				}
			}
		});

		b_prev.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				clearSelection();
				Label l = (Label) yearGrid.getWidget(0, 0);
				if (l == null) {
					return;
				}
				int first = Integer.parseInt(l.getText());
				fillYearGrid(first - 12, first - 1);
			}
		});
		b_next.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				clearSelection();
				Label l = (Label) yearGrid
						.getWidget(LAST_DISPLAYED_YEAR_ROW, 1);
				if (l == null) {
					return;
				}
				int last = Integer.parseInt(l.getText());
				fillYearGrid(last + 1, last + 12);
			}
		});
	}

	private void closePopups() {
		yearPopup.setVisible(false);
		monthPopup.setVisible(false);
	}

	private void selectWidget(Widget l) {
		if (l == null) {
			return;
		}
		l.addStyleName(theme.getSelectedYearMonthLabelStyle().getClassName());
	}

	private void clearSelection() {
		for (int i = 0; i < yearGrid.getRowCount(); i++) {
			if (i > LAST_DISPLAYED_YEAR_ROW) {
				continue;
			}
			for (int j = 0; j < yearGrid.getColumnCount(); j++) {
				Widget l = yearGrid.getWidget(i, j);
				deselectWidget(l);
			}
		}
	}

	private void deselectWidget(Widget l) {
		if (l == null) {
			return;
		}
		l
				.removeStyleName(theme.getSelectedYearMonthLabelStyle()
						.getClassName());
	}

	private boolean isSelected(Label l) {
		if (l == null) {
			return false;
		}
		return l.getStyleName().contains(
				theme.getSelectedYearMonthLabelStyle().getClassName());
	}

	private int getDisplayedYear() {
		return model.getDisplayedYear();
	}

	private int getDisplayedMonth() {
		return model.getDisplayedMonth();
	}

	private int getRowForMonth(int month) {
		if (month < 6) {
			return month;
		} else {
			return month - 6;
		}
	}

	private int getColForMonth(int month) {
		if (month < 6) {
			return 0;
		} else {
			return 1;
		}
	}

	private Label getMonthLabel(String month) {
		for (int i = 0; i < monthGrid.getRowCount(); i++) {
			for (int j = 0; j < monthGrid.getColumnCount(); j++) {
				Label l = (Label) monthGrid.getWidget(i, j);
				if (l.getText().equals(month) == true) {
					return l;
				}
			}
		}
		return null;
	}

	private Label getYearLabel(int year) {
		for (int i = 0; i < yearGrid.getRowCount(); i++) {
			if (i > LAST_DISPLAYED_YEAR_ROW) {
				continue;
			}
			for (int j = 0; j < yearGrid.getColumnCount(); j++) {
				Label l = (Label) yearGrid.getWidget(i, j);
				if (Integer.parseInt(l.getText()) == year) {
					return l;
				}
			}
		}
		return null;
	}

	private int getSelectedYear(int row, int col) {
		Label l = (Label) yearGrid.getWidget(row, col);
		if (l == null) {
			return -1;
		}
		return Integer.parseInt(l.getText());
	}

	private int getSelectedMonth(int row, int col) {
		if (col == 0) {
			return row;
		} else if (col == 1) {
			return 6 + row;
		}
		return -1;
	}

	@Override
	public void update(ICalendarContext context) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onKeyDown(KeyDownEvent event) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onKeyUp(KeyUpEvent event) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onMouseWheel(MouseWheelEvent event) {
		// TODO Auto-generated method stub
	}
}
